package com.ep.thirdPlatformProject.system.aop;

import cn.hutool.core.util.StrUtil;
import com.ep.thirdPlatformProject.job.common.AjaxResult;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.cloud.commons.util.InetUtils;
import org.springframework.cloud.commons.util.InetUtilsProperties;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

@Component
@Aspect
@Slf4j
@RequiredArgsConstructor
public class ApiLimitAspect {


    private static final String LIMIT_REQUIRES = "limit:require:";


    private final RedisTemplate<String, Object> redisTemplate;

    @Around("@annotation(ApiLimit)")
    public Object doAround(ProceedingJoinPoint point) throws Throwable {
        // 获取方法签名
        MethodSignature signature = (MethodSignature) point.getSignature();
        Object result = null;
        try (InetUtils inetUtils = new InetUtils(new InetUtilsProperties())) {
            // 获取第一个非回环地址的 IP 地址
            String ipAddress = inetUtils.findFirstNonLoopbackHostInfo().getIpAddress();
            // 获取 ApiLimit 注解信息
            ApiLimit annotation = signature.getMethod().getAnnotation(ApiLimit.class);
            // 获取限制次数
            int count = annotation.count();
            // 获取时间限制
            int timeLimit = annotation.timeLimit();
            // 获取时间单位
            TimeUnit timeUnit = annotation.timeUnit();
            // 构建 Redis 键
            String redisKey = LIMIT_REQUIRES + ipAddress + ":" + signature.getMethod().getName();
            // 从 Redis 中获取访问信息
            Boolean hasKey = redisTemplate.hasKey(redisKey);
            // 初始化
            if (Boolean.FALSE.equals(hasKey)) {
                // 获取当前时间戳
                long now = System.currentTimeMillis();
                // 创建存储访问信息的 HashMap
                Map<String, Object> map = new HashMap<>();
                // 初始访问次数设置为 1
                map.put("times", 1);
                // 存储当前时间
                map.put("visitTime", now);
                // 将访问信息存储到 Redis 中
                redisTemplate.boundHashOps(redisKey).putAll(map);
                // 设置 Redis 键的过期时间
                redisTemplate.expire(redisKey, timeLimit, timeUnit);

            } else {
                String timesValue = Objects.requireNonNull(redisTemplate.boundHashOps(redisKey).get("times")).toString();
                // 当访问次数超过限制，返回错误信息
                if (Integer.parseInt(Objects.requireNonNull(timesValue)) >= count) {
                    return AjaxResult.error(500, "当前访问次数以达到最大访问次数限制"+ timeLimit);
                    // 访问次数+1
                } else {
                    redisTemplate.boundHashOps(redisKey).put("visitTime", System.currentTimeMillis());
                    // 将访问次数加 1
                    redisTemplate.boundHashOps(redisKey).increment("times", 1);
                }
            }

            result = point.proceed();
        } catch (Exception e) {
            log.error("限流异常", e);
            throw new RuntimeException("限流异常");
        }
        return result;
    }


}
